package org.windowkit.toolkit.widget.text;

import java.util.ArrayList;

import javax.swing.JTextField;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.PlainDocument;

/**
 * Extension of JTextField with undo support.
 */
public class TextField extends JTextField implements UndoableEditListener {
	private static final long serialVersionUID = 8720935456106490302L;

	private ArrayList<String> undoTexts;
	private boolean inUndo;

	/**
	 * Constructor.
	 */
	public TextField() {
		super();
		init();
	}

	/**
	 * Constructor.
	 * @param doc document
	 * @param text initial text
	 * @param columns columns
	 */
	public TextField(final Document doc, final String text, final int columns) {
		super(doc, text, columns);
		init();
	}

	/**
	 * Constructors.
	 * @param columns columns
	 */
	public TextField(final int columns) {
		super(columns);
		init();
	}

	/**
	 * Constructor.
	 * @param text initial text
	 */
	public TextField(final String text) {
		super(text);
		init();
	}

	/**
	 * Constructor.
	 * @param text initial text
	 * @param columns columns
	 */
	public TextField(final String text, final int columns) {
		super(text, columns);
		init();
	}

	/**
	 * Initialize this component.
	 */
	protected void init() {
		undoTexts = new ArrayList<String>();
		getDocument().addUndoableEditListener(this);
	}

	/**
	 * Set the maximum amount of characters allowed by this text field.
	 *
	 * @param length
	 *            The maximum number of characters the user is allowed to enter.
	 */
	public void setMaxLength(final int length) {
		this.setDocument(new LimitedSizeDocument(length));
		getDocument().addUndoableEditListener(this);
	}

	public void undo() {
		if (hasundo()) {
			inUndo = true;
			setText(undoTexts.remove(undoTexts.size() - 1));
			inUndo = false;
		}
	}

	public boolean hasundo() {
		return undoTexts.size() > 0 ? true : false;
	}


	public void replaceSelection(final String text, final boolean undoable) {
		undoTexts.add(getText());
		replaceSelection(text);
	}

	public boolean editAllowed() {
		return isEditable();
	}

	/**
	 * {@inheritDoc}
	 */
	public void undoableEditHappened(final UndoableEditEvent e) {
		if (inUndo || (undoTexts.indexOf(getText()) == undoTexts.size() - 1)) {
			return;
		}
		undoTexts.add(getText());
	}

	/**
	 * Limits the amount of characters the user may enter in the text field.
	 * @author Eric Lundin
	 */
	class LimitedSizeDocument extends PlainDocument {
		private static final long serialVersionUID = -4181684512963826831L;
		private final int size;

		/**
		 * Constructor.
		 * @param size The maximum number of characters the user may enter.
		 */
		public LimitedSizeDocument(final int size) {
			this.size = size;
		}

		@Override
	    public void insertString(final int offs, final String str, final AttributeSet a)
				throws BadLocationException {
			int spaceLeft;
			if (str == null || this.getLength() >= size) {
				return;
			}
			spaceLeft = size - (this.getLength());
			if (spaceLeft > str.length()) {
				spaceLeft = str.length();
			}
			super.insertString(offs, str.substring(0, spaceLeft), a);
		}
	}

}
